#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"
#include <typeinfo>

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
  this->variable=variable;
  this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) : NoeudOperateurUnitaire(operateur, operandeDroit){
    this->operandeGauche=operandeGauche;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() { 
  int valeur=0;
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();
  if      (this->operateur=="+") valeur = og+od; 
  else if (this->operateur=="-") valeur = og-od;
  else if (this->operateur=="*") valeur = og*od; 
  else if (this->operateur=="==") valeur = og==od;
  else if (this->operateur=="!=") valeur = og!=od;
  else if (this->operateur=="<") valeur = og<od;
  else if (this->operateur=="<=") valeur = og<=od;
  else if (this->operateur==">") valeur = og>od;
  else if (this->operateur==">=") valeur = og>=od;
  else if (this->operateur=="et") valeur = og&&od;
  else if (this->operateur=="ou") valeur = og||od;
  else if (this->operateur=="/"){
	if (od!=0)
		valeur = og/od;
  	else {
	  	cout << "Erreur pendant l'interpretation : division par zero" << endl;
	  	exit(0); // plus tard on levera une exception
  	}
  return valeur;
  }
  return 0;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurUnitaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurUnitaire::NoeudOperateurUnitaire(Symbole operateur, Noeud* operandeDroit){
    this->operandeDroit=operandeDroit;
    this->operateur=operateur;
}
////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurUnitaire::getValeur(){
    if(operateur=="non")
        return !operandeDroit->getValeur();
    else if (operateur=="-")
        return -operandeDroit->getValeur();
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurUnitaire::afficher(unsigned short indentation){
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Unitaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeDroit->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudStructureControl
////////////////////////////////////////////////////////////////////////////////
NoeudStructureControl::NoeudStructureControl(string n) : nom(n){
}

int NoeudStructureControl::getValeur(){
    while(condition->getValeur())
        seqInst->getValeur();
    return 0;
}
////////////////////////////////////////////////////////////////////////////////

void NoeudStructureControl::afficher(unsigned short indentation){
    Noeud::afficher(indentation);
    cout << "Noeud - Structure de control "<< nom << endl;
    Noeud::afficher(indentation+1);
    cout << "condition:" << endl;
    condition->afficher(indentation+1);
    Noeud::afficher(indentation+1);
    cout << "sequence d'inst.:" << endl;
    seqInst->afficher(indentation+1);
}
////////////////////////////////////////////////////////////////////////////////
void NoeudStructureControl::ajouteCondition(Noeud* condition){
    this->condition=condition;
}

void NoeudStructureControl::ajouteSeqInt(Noeud*seqInst){
    this->seqInst=seqInst;
}
////////////////////////////////////////////////////////////////////////////////
// NoeudSi
////////////////////////////////////////////////////////////////////////////////
NoeudSi::NoeudSi(Noeud* conditionPrincipal, Noeud* seqInstPrincipal) : NoeudStructureControl(string("si")),
        sinonsiCondition(),sinonsiSeqInst()
{
    ajouteCondition(conditionPrincipal);
    ajouteSeqInt(seqInstPrincipal);
    bSinon = false;
}
////////////////////////////////////////////////////////////////////////////////
int NoeudSi::getValeur(){
    int valeur = 0;
    if(condition->getValeur())
        seqInst->getValeur();
    else{
        for (unsigned int i=0; i<sinonsiCondition.size() && valeur==0; i++){
            if(sinonsiCondition[i]->getValeur()){
                sinonsiSeqInst[i]->getValeur();
                valeur = 1;
            }
        }
        if(!valeur && bSinon)
            sinon->getValeur();
    }
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudSi::afficher(unsigned short indentation){
    Noeud::afficher(indentation);
    cout << "Noeud - Structure de controle si" << endl;
    condition->afficher(indentation+1);
    seqInst->afficher(indentation+1);
    for(unsigned int i=0;i<sinonsiCondition.size();i++){
        Noeud::afficher(indentation);
        cout << "sinonsi" << endl;
        sinonsiCondition[i]->afficher(indentation+1);
        sinonsiSeqInst[i]->afficher(indentation+1);
    }
    if(bSinon){
        Noeud::afficher(indentation);
        cout << "sinon" << endl;
        sinon->afficher(indentation+1);
    }
}
////////////////////////////////////////////////////////////////////////////////
void NoeudSi::ajouteSinonsiCondition(Noeud* sinonsiCondition){
    this->sinonsiCondition.push_back(sinonsiCondition);
}
////////////////////////////////////////////////////////////////////////////////
void NoeudSi::ajouteSinonsiSeqInst(Noeud* sinonsiSeqInst){
    this->sinonsiSeqInst.push_back(sinonsiSeqInst);
}
////////////////////////////////////////////////////////////////////////////////
void NoeudSi::ajouteSinon(Noeud* sinon){
    this->sinon=sinon;
    bSinon = true;
}
////////////////////////////////////////////////////////////////////////////////
// NoeudFaireJusqua
////////////////////////////////////////////////////////////////////////////////
NoeudFaireJusqua::NoeudFaireJusqua():NoeudStructureControl("faire-jusqua"){
}
////////////////////////////////////////////////////////////////////////////////
int NoeudFaireJusqua::getValeur(){
    seqInst->getValeur();
    NoeudStructureControl::getValeur();
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
// NoeudPour
////////////////////////////////////////////////////////////////////////////////
NoeudPour::NoeudPour():NoeudStructureControl("pour"),initialization(),modificateur(){
}
////////////////////////////////////////////////////////////////////////////////
int NoeudPour::getValeur(){
    for(unsigned int i=0;i<initialization.size();i++)
        initialization[i]->getValeur();
        while(condition->getValeur()){
            seqInst->getValeur();
            for(unsigned int i=0;i<modificateur.size();i++)
                modificateur[i]->getValeur();
        }
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudPour::afficher(unsigned short indentation){
    Noeud::afficher(indentation);
    cout << "Noeud - Structure de control pour" << endl;
    for(unsigned int i=0;i<initialization.size();i++)
        initialization[i]->afficher(indentation+1); 
        condition->afficher(indentation+1);
    for(unsigned int i=0;i<modificateur.size();i++)
        modificateur[i]->afficher(indentation+1);
    seqInst->afficher(indentation+1);
}
////////////////////////////////////////////////////////////////////////////////
void NoeudPour::ajouteInitialization(Noeud* initialisation){
    this->initialization.push_back(initialisation);
}
////////////////////////////////////////////////////////////////////////////////
void NoeudPour::ajouteModificateur(Noeud* modificateur){
    this->modificateur.push_back(modificateur);
}
////////////////////////////////////////////////////////////////////////////////
// NoeudLire
////////////////////////////////////////////////////////////////////////////////
NoeudLire::NoeudLire(Noeud* v):variable(v){
}
////////////////////////////////////////////////////////////////////////////////
int NoeudLire::getValeur(){
    string strValeur;
    cin>>strValeur;
    ((SymboleValue*)variable)->setValeur(atoi(strValeur.c_str()));
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudLire::afficher(unsigned short indentation){
    Noeud::afficher(indentation);
    cout << "Noeud - Lire" << endl;
    variable->afficher(indentation+1);
}
////////////////////////////////////////////////////////////////////////////////
// NoeudEcrire
////////////////////////////////////////////////////////////////////////////////
NoeudEcrire::NoeudEcrire(Noeud* s):sortie(s){
}
////////////////////////////////////////////////////////////////////////////////
int NoeudEcrire::getValeur(){
    if (typeid (*sortie) == typeid (SymboleValue) && (*(SymboleValue*) sortie) == "<CHAINE>")
        cout << ((SymboleValue*) sortie)->getChaine();

    else {
        int valeur = sortie->getValeur();
        cout << valeur << endl;
    }
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudEcrire::afficher(unsigned short indentation){
    Noeud::afficher(indentation);
    cout << "Noeud - Ecrire" << endl;
    sortie->afficher(indentation+1);
}
////////////////////////////////////////////////////////////////////////////////


